import pygame

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

from visualizer import Visualizer
from world import World
from vec3 import Vec3
from sphere import Sphere
from sheet import Sheet
from rigidJoint import RigidJoint

import time
import math

import random

class GLVisualizer(Visualizer):
    def renderLine(self, v1, v2):
        glColor3f(0,0,1)
        glBegin(GL_LINES)
        glVertex3f(v1.x,v1.y,v1.z)
        glVertex3f(v2.x,v2.y,v2.z)
        glEnd()

    def renderSphere(self, pos, r):
        glColor3f(0,1,0)
        glPushMatrix()
        glTranslatef(pos.x,pos.y,pos.z)
        glutSolidSphere(r,10,10)
        glPopMatrix()

class App:
    def __init__(self):
        self.pushForce = 100

        self.initGraphics()
        self.initPhysics()

    def initPhysics(self):
        self.world = World()
        self.world.visualizers.append(GLVisualizer())
        self.world.gravity = Vec3(0,-10,0)
        
        stiffness = 30
        
        for i in range(1):
            self.world.bodies.append(Sphere(r = 10,
                                            pos = Vec3((i % 2) * 5, 20 + 25 * i, 0),
                                            stiffness = stiffness,
                                            stacks = 5,
                                            slices = 5,
                                            mass = 1))
##            if i > 0:
##                self.world.bodies.append(RigidJoint(self.world.bodies[-1],
##                                                    self.world.bodies[-2],
##                                                    stiffness = stiffness))

##        self.world.bodies.append(Sheet(mass = 10,
##                                       width = 50,
##                                       height = 50,
##                                       rows = 10,
##                                       cols = 10,
##                                       pos = Vec3(0,150,0),
##                                       stiffness = stiffness))

        #s1 = Sphere(r = 10, pos = Vec3(0,20,0), stiffness = stiffness)
        #s2 = Sphere(r = 10, pos = Vec3(-30,20,30), stiffness = stiffness)
        #s3 = Sphere(r = 10, pos = Vec3(30,20,30), stiffness = stiffness)
        
        #j = RigidJoint(s2,s3, stiffness = 50)
        
        #self.world.bodies.extend([s1,s2,s3,j])
        
        #j2 = RigidJoint(s1,s2,stiffness = 1)
        #j3 = RigidJoint(s1,s3,stiffness = 1)
        #self.world.bodies.extend([j2,j3])

        self.pushing = False

    def initGraphics(self):
        self.cameraYaw = 0
        self.cameraYawSpeed = 0
        self.cameraHeight = 50
        self.cameraHeightSpeed = 0
        self.cameraDist = 100
        self.cameraDistSpeed = 0

        pygame.init()
        glutInit(())

        flags = pygame.OPENGL | pygame.DOUBLEBUF | pygame.HWSURFACE

        self.screen = pygame.display.set_mode((640,480),flags)

        glEnable(GL_DEPTH_TEST)
        glShadeModel(GL_SMOOTH)
        glLineWidth(2)
        glClearColor(1,1,1,1)

    def pumpInput(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.shutdown = True
                
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    self.cameraHeightSpeed = 10
                elif event.key == pygame.K_DOWN:
                    self.cameraHeightSpeed = -10

                elif event.key == pygame.K_LEFT:
                    self.cameraYawSpeed = 3
                elif event.key == pygame.K_RIGHT:
                    self.cameraYawSpeed = -3

                elif event.key == pygame.K_PAGEUP:
                    self.cameraDistSpeed = -10
                elif event.key == pygame.K_PAGEDOWN:
                    self.cameraDistSpeed = 10

                elif event.key == pygame.K_SPACE:
                    self.pushing = not self.pushing

            elif event.type == pygame.KEYUP:
                if event.key in (pygame.K_UP, pygame.K_DOWN):
                    self.cameraHeightSpeed = 0
                elif event.key in (pygame.K_LEFT, pygame.K_RIGHT):
                    self.cameraYawSpeed = 0
                elif event.key in (pygame.K_PAGEUP, pygame.K_PAGEDOWN):
                    self.cameraDistSpeed = 0

    def tick(self, dt):
        self.world.tick(dt)

        self.cameraYaw += self.cameraYawSpeed * dt
        self.cameraHeight += self.cameraHeightSpeed * dt
        self.cameraDist += self.cameraDistSpeed * dt

        if self.pushing:
            topPoint = None
            for point in self.world.bodies[0].pointMasses:
                if not topPoint or point.pos.y > topPoint.pos.y:
                    topPoint = point

            if topPoint:
                f = (self.getCamPos() * Vec3(1,0,1) - topPoint.pos).norm() * self.pushForce
                topPoint.applyForce(f)

    def getCamPos(self):
        camx = self.cameraDist * math.cos(self.cameraYaw)
        camz = self.cameraDist * math.sin(self.cameraYaw)
        return Vec3(camx,self.cameraHeight,camz)

    def render(self):
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)

        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45.0,800.0/600.0,1,1000)

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

        cam = self.getCamPos()

        gluLookAt(cam.x,cam.y,cam.z,0,0,0,0,1,0)

        self.world.render()

        glFlush()
        pygame.display.flip()

    def run(self):
        oneTickTime = 0.01
        lastTime = startTime = time.time()
        frameCount = 0
        self.shutdown = False
        minFrameTime = 0.02

        while not self.shutdown:
            thisTime = time.time()
            if thisTime - lastTime >= minFrameTime:
                lastTime += minFrameTime
                dtime = minFrameTime

                self.pumpInput()
                self.tick(dtime)
                self.render()

                frameCount += 1

        totalTime = time.time() - startTime
        print "%d frames in %f secs = %f fps" % (frameCount, totalTime, float(frameCount) / totalTime)

        pygame.quit()

if __name__ == "__main__":
    App().run()
